/* * Sun Public License Notice * * The contents of this file are subject to the Sun Public License * Version 1.0 (the "License"). You may not use this file except in * compliance with the License. A copy of the License is available at * http://www.sun.com/ * * The Original Code is Forte for Java, Community Edition. The Initial * Developer of the Original Code is Sun Microsystems, Inc. Portions * Copyright 1997-2000 Sun Microsystems, Inc. All Rights Reserved. */ package org.openide.text; import java.util.*; import java.io.*; import org.openide.util.RequestProcessor; /** Class that holds line information about one document. * Defines operations that can be executed on the objects, the implementation * can change when we find that it is too slow. * * @author Jaroslav Tulach */ final class LineStruct extends Object { /** max number of lines to work with */ private static final int MAX = Integer.MAX_VALUE / 2; /** Holding the original and current number of lines. */ private static final class Info extends Object { /** constants for distintion of the type of info */ public static final int AREA_ORIGINAL = 0; public static final int AREA_INSERT = 1; public static final int AREA_REMOVE = -1; /** original number */ public int original; /** current number */ public int current; public Info (int o, int c) { original = o; current = c; } /** Finds the type. */ public int type () { if (current == original) return AREA_ORIGINAL; if (current == 0) return AREA_REMOVE; if (original == 0) return AREA_INSERT; throw new InternalError("Original: " + original + " current: " + current); // NOI18N } /** Performs insert on this Info object. * @param pos position to insert to * @param count how much objects to insert * @param it iterator that just returned this object * @return how much lines to insert after this object */ public int insert (int pos, int count, ListIterator it) { switch (type ()) { case AREA_INSERT: // insert area, add to it all current += count; return 0; case AREA_ORIGINAL: if (pos == current) { // if the insert position is at the end, // then let all the characters be added by next // item return count; } if (pos == 0) { // prepend the insert area before the current // Info in the chain Info ni = new Info (original, original); original = 0; current = count; it.add (ni); // everything has been prepended return 0; } // we have to devided the interval to two parts // and insert insert block between them Info ni = new Info (original - pos, original - pos); // the area from 0 to pos original = current = pos; // insert the insert area it.add (new Info (0, count)); // the rest of the area it.add (ni); return 0; case AREA_REMOVE: // supposing that pos == 0 if (pos != 0) { throw new IllegalStateException ("Pos: " + pos); // NOI18N } // check the previous Info if it cannot be merged Info prev = (Info)it.previous (); // current item // prev.type () == AREA_REMOVE => prev is used // only if the it.hasPrevious is true and prev // is changed again if (it.hasPrevious ()) { prev = (Info)it.previous (); // previous it.next (); // previous } it.next (); // current // inserted lines can be put instead of the orignal // ones if (count < original) { if (prev.type () == AREA_ORIGINAL) { prev.original += count; prev.current += count; // modify this remove object original -= count; } else { ni = new Info (original - count, 0); // turn this to regular part original = current = count; // insert the new delete part it.add (ni); } // everything processed return 0; } else { if (prev.type () == AREA_ORIGINAL) { prev.current += original; prev.original += original; it.remove (); return count - original; } else { // turn whole delete part to regular one current = original; // the rest of characters to proceed return count - current; } } default: throw new IllegalStateException ("Type: " + type ()); // NOI18N } } /** A method that handles the delete operation. * @param pos position in the Info block where delete started * @param info * info.original the amount of lines to be deleted * info.current the amount of lines that should be later marked as deleted * @param it the iterator that previously returned this instance * @return * info.original the amount of lines to be yet deleted * info.current the amount of lines that needs to be later marked as deleted * this will be put before the */ public Info delete (int pos, Info info, ListIterator it) { switch (type ()) { case AREA_ORIGINAL: if (pos != 0) { // specials int size = current - pos; current = original = pos; if (size >= info.original) { // delete is whole only in this block Info ni = new Info (size, size); it.add (ni); info.current += info.original; info.original = 0; return info; } else { // something is resting after this block info.original -= size; info.current += size; return info; } } else { // deleting from first position if (current >= info.original) { // something is resting from me (at the end) // number of lines to mark as deleted info.current += info.original; // number of lines in this block is decreased current -= info.original; original = current; // number of lines to be yet deleted info.original = 0; return info; } else { // I am completelly deleted it.remove (); // number of lines to mark as deleted info.current += current; info.original -= current; return info; } } case AREA_INSERT: if (pos != 0) { // specials int size = current - pos; if (size >= info.original) { // delete is whole only in this block current -= info.original; info.original = 0; return info; } else { // something is resting after this block current = pos; info.original -= size; return info; } } else { // deleting from first position if (current >= info.original) { // something is resting from me (at the end) // number of lines in this block is decreased current -= info.original; // number of lines to be yet deleted info.original = 0; it.remove (); return info; } else { // I am completelly deleted it.remove (); // how much lines to be deleted yet info.original -= current; return info; } } case AREA_REMOVE: // only derease the number of lines that needs to be deleted // because this area can absorb some original += info.current; info.current = 0; return info; default: throw new InternalError("Type: " + type ()); // NOI18N } } } /** processor for all requests */ private static final RequestProcessor PROCESSOR = new RequestProcessor (); /** list of Info objects that represents the whole document * @associates Info*/ private LinkedList list; /** Constructor. */ public LineStruct () { list = new LinkedList (); list.add (new Info (MAX, MAX)); } /** Converts original numbering to the new one. * @param line the line number in the original * @return line number in the new numbering */ public int originalToCurrent (int line) { // class to compute in the request processor thread class Compute extends Object implements Runnable { public int result; public Compute (int i) { result = i; } public void run () { result = originalToCurrentImpl (result); } } Compute c = new Compute (line); // post the computation and wait till it is finished PROCESSOR.post (c).waitFinished (); // return result return c.result; } /** Inserts line(s) at given position. * @param line the line number in current numbering * @param count number of lines inserted */ public void insertLines (final int line, final int count) { PROCESSOR.post (new Runnable () { public void run () { insertLinesImpl (line, count); } }); } /** Method that deletes some lines in the current state of * the document. * * @param line the line number in current numbering * @param */ public void deleteLines (final int line, final int count) { PROCESSOR.post (new Runnable () { public void run () { deleteLinesImpl (line, count); } }); } /** Converts original numbering to the new one. * @param line the line number in the original * @return line number in the new numbering */ private int originalToCurrentImpl (int line) { Iterator it = list.iterator (); int cur = 0; for (;;) { Info i = (Info)it.next (); if (i.original > line) { // ok we found the segment that contained this line return line > i.current ? cur + i.current : cur + line; } cur += i.current; line -= i.original; } } /** Inserts line(s) at given position. * @param line the line number in current numbering * @param count number of lines inserted */ private void insertLinesImpl (int line, int count) { ListIterator it = list.listIterator (); for (;;) { Info i = (Info)it.next (); if (i.current >= line) { for (;;) { count = i.insert (line, count, it); if (count == 0) { return; } i = (Info)it.next (); line = 0; } } line -= i.current; } } /** Method that deletes some lines in the current state of * the document. * * @param line the line number in current numbering * @param */ private void deleteLinesImpl (int line, int count) { ListIterator it = list.listIterator (); for (;;) { Info i = (Info)it.next (); if (i.current >= line) { // information to hold both the number of lines to delete (original) // and the number of lines to mark as delete at the end (current) Info stat = new Info (count, 0); for (;;) { stat = i.delete (line, stat, it); if (stat.original == 0) { break; } i = (Info)it.next (); line = 0; } // insert the amount of lines to mark deleted before current position if (stat.current > 0) { Info prev = (Info)it.previous (); boolean hasPrev = it.hasPrevious (); if (hasPrev) { prev = (Info)it.previous (); } if (prev.current == 0) { prev.original += stat.current; } else { if (hasPrev) { it.next (); } it.add (new Info (stat.current, 0)); } } return; } line -= i.current; } } /** Prints the struct. * public void print () { PROCESSOR.post (new Runnable () { public void run () { printImpl (); } }); } /** Prints the lines in the current stream * private void printImpl () { Iterator it = list.iterator (); System.out.println("vvvvvvvvvvv"); while (it.hasNext ()) { Info i = (Info)it.next (); switch (i.type ()) { case Info.AREA_INSERT: System.out.print("Ins: "); break; case Info.AREA_REMOVE: System.out.print("Rem: "); break; case Info.AREA_ORIGINAL: System.out.print("Org: "); break; } System.out.println(i.current + " - " + i.original); } System.out.println("^^^^^^^^^^^"); } public static void main (String[] args) throws Exception { LineStruct ls = new LineStruct (); BufferedReader r = new BufferedReader (new FileReader ("z:/X")); try { for (;;) { String s = r.readLine (); if (s == null) break; StringTokenizer st = new StringTokenizer (s); String pl = st.nextToken (); String s1 = st.nextToken (); String s2 = st.nextToken (); int i1 = Integer.valueOf (s1).intValue (); int i2 = Integer.valueOf (s2).intValue (); if (pl.equals ("+")) { ls.insertLinesImpl (i1, i2); } else { if (pl.equals ("?")) { int rr = ls.originalToCurrentImpl (i1); System.out.println("Orig: " + i1 + " now: " + rr); continue; } else { ls.deleteLinesImpl (i1, i2); } } ls.printImpl (); } } catch (NoSuchElementException ex) { System.out.println("No such element"); ex.printStackTrace(); } } */ } /* * Log * 6 Gandalf 1.5 1/13/00 Ian Formanek NOI18N * 5 Gandalf 1.4 10/22/99 Ian Formanek NO SEMANTIC CHANGE - Sun * Microsystems Copyright in File Comment * 4 Gandalf 1.3 10/10/99 Petr Hamernik console debug messages * removed. * 3 Gandalf 1.2 8/11/99 Jaroslav Tulach Survives when the * document becomes empty. * 2 Gandalf 1.1 7/30/99 Jaroslav Tulach getOriginal & getCurrent * in Line * 1 Gandalf 1.0 7/27/99 Jaroslav Tulach * $ */